package edu.arizona.sista.learn.activity.model;

import java.util.Map;

import edu.arizona.sista.learn.activity.model.args.PredArg;


public class Relation extends Predicate {
	
	private final RelationType _relationType;
	
	/* Threshold window size.
	 * 1 seems to work best for now as we're still getting way too many
	 * false positives.  When detections get less chatty, we may consider
	 * raising this window size.
	 */
	public static int WINDOW_SIZE = 1;		
	
	
	public Relation(RelationType relationType, Predicate arg1, Predicate arg2) {
		super(relationType.toString(), arg1, arg2);
//		Collections.copy(_relations, relations);
		_relationType = relationType;
		_type = "relation";
	}
	
	public RelationType getRelationsType(){
		return _relationType;
	}
	
	public boolean check(PredicateInterval predInt1, PredicateInterval predInt2){
		return check(_relationType, predInt1, predInt2);
	}
	
	public PredicateInterval getInterval(PredicateInterval predInt1, PredicateInterval predInt2){
		return getInterval(_relationType, predInt1, predInt2);
	}
	
	@Override
	public Predicate[] getArgs(){
		//return (Predicate[])_args; //this doesn't work
		Predicate[] args = new Predicate[_args.length];
		for( int i=0; i<_args.length; i++ )
			args[i] = (Predicate) _args[i];
		
		return args;
	}
	
	@Override
	public Predicate getArg(int index){
		return getArgs()[index];
	}
	
	@Override
	public Relation recastCopy(Map<PredArg,PredArg> varSubs) {
		return new Relation(this._relationType,
				((Predicate)this._args[0]).recastCopy(varSubs),
				((Predicate)this._args[1]).recastCopy(varSubs)
		);
	}
	
	public static boolean check(RelationType type, PredicateInterval predInt1, PredicateInterval predInt2){
		return getInterval(type, predInt1, predInt2) != null;
	}
	
	public static PredicateInterval getInterval(RelationType type, PredicateInterval predInt1, PredicateInterval predInt2){
		
		switch(type){
		
		case ol:	//older than	
			if( predInt1.start < predInt2.start )
				return new PredicateInterval( new Predicate("ol", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.start, predInt2.start);
			break;
		case yo:	//younger than
			if( predInt1.start > predInt2.start )
				return new PredicateInterval( new Predicate("yo", predInt1.getPredicate(), predInt2.getPredicate()), predInt2.start, predInt1.start);
			break;
		case hh:	//head-to-head with
			if( predInt1.start == predInt2.start )
				return new PredicateInterval( new Predicate("hh", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.start, predInt2.start);
			break;
		case sv:	//survives
			if( predInt1.end > predInt2.end )
				return new PredicateInterval( new Predicate("sv", predInt1.getPredicate(), predInt2.getPredicate()), predInt2.end, predInt1.end);
			break;
		case sb:	//survived by
			if( predInt1.end < predInt2.end )
				return new PredicateInterval( new Predicate("sb", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.end, predInt2.end);
			break;
		case tt:	//tail-to-tail with
			if( predInt1.end == predInt2.end )
				return new PredicateInterval( new Predicate("tt", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.end, predInt2.end);
			break;
		case pr:	//precedes
			if( predInt1.end <= predInt2.start )
				return new PredicateInterval( new Predicate("pr", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.end, predInt2.start);
			break;
		case sd:	//succeeds
			if( predInt1.start >= predInt2.end )
				return new PredicateInterval( new Predicate("sd", predInt1.getPredicate(), predInt2.getPredicate()), predInt2.end, predInt1.start);
			break;
		case ct:	//contemporary with (concurrent with)
			if( predInt1.start <= predInt2.end && predInt1.end >= predInt2.start)
				return new PredicateInterval( new Predicate("ct", predInt1.getPredicate(), predInt2.getPredicate()), Math.max(predInt1.start, predInt2.start), Math.min(predInt1.end, predInt2.end));
			break;
		case bd:	//born before death of
			if( predInt1.start < predInt2.end )
				return new PredicateInterval( new Predicate("bd", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.start, predInt2.end);
			break;
		case db:	//died after birth of
			if( predInt1.end > predInt2.start )
				return new PredicateInterval( new Predicate("db", predInt1.getPredicate(), predInt2.getPredicate()), predInt2.start, predInt1.end);
			break;
			
		//made these up
		case oh:	//older than or head to head
			if( predInt1.start <= predInt2.start )
				return new PredicateInterval( new Predicate("oh", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.start, predInt2.start);
			break;
		case st:	//survived by or tail to tail
			if( predInt1.end <= predInt2.end )
				return new PredicateInterval( new Predicate("st", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.end, predInt2.end);
			break;
		//combinations:
//		case ob:	//older than & survived by
//		case ys:	//younger than & survives
//		case oc:	//older than & contemporary with
//		case yc:	//younger than & contemporary with
//		case sc:	//survives & contemporary with
//		case bc:	//survived by & contemporary with
		
		//Allen interval relations (some also combinations of Freska)
		case lt:	//before (less than)
			if( predInt1.end < predInt2.start )
				return new PredicateInterval( new Predicate("lt", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.end, predInt2.start);
			break;
		case gt:	//after (greater than)
			if( predInt1.start > predInt2.end )
				return new PredicateInterval( new Predicate("gt", predInt1.getPredicate(), predInt2.getPredicate()), predInt2.end, predInt1.start);
			break;
		case eq:	//equals
			if( predInt1.start == predInt2.start && predInt1.end == predInt2.end)
				return new PredicateInterval( new Predicate("eq", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.start, predInt1.end);
			break;
		case m:		//meets
			if( predInt1.end == predInt2.start)
				return new PredicateInterval( new Predicate("m", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.end, predInt2.start);
			break;
		case mi:	//met by
			if( predInt1.start == predInt2.end)
				return new PredicateInterval( new Predicate("eq", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.start, predInt2.end);
			break;
		case o:		//overlaps
			if( predInt1.end > predInt2.start)
				return new PredicateInterval( new Predicate("o", predInt1.getPredicate(), predInt2.getPredicate()), predInt2.start, predInt1.end);
			break;
		case oi:	//overlapped by
			if( predInt1.start < predInt2.end)
				return new PredicateInterval( new Predicate("oi", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.start, predInt2.end);
			break;
		case d:		//during
			if( predInt1.start > predInt2.start && predInt1.end < predInt2.end)
				return new PredicateInterval( new Predicate("d", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.start, predInt1.end);
			break;
		case di:	//inverse during
			if( predInt1.start < predInt2.start && predInt1.end > predInt2.end)
				return new PredicateInterval( new Predicate("di", predInt1.getPredicate(), predInt2.getPredicate()), predInt2.start, predInt2.end);
			break;
		case s:		//starts
			if( predInt1.start == predInt2.start && predInt1.end < predInt2.end)
				return new PredicateInterval( new Predicate("s", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.start, predInt1.end);
			break;
		case si:	//started by
			if( predInt1.start == predInt2.start && predInt1.end > predInt2.end)
				return new PredicateInterval( new Predicate("si", predInt1.getPredicate(), predInt2.getPredicate()), predInt2.start, predInt2.end);
			break;
		case f:		//finishes
			if( predInt1.start > predInt2.start && predInt1.end == predInt2.end)
				return new PredicateInterval( new Predicate("f", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.start, predInt1.end);
			break;
		case fi:	//finished by
			if( predInt1.start < predInt2.start && predInt1.end == predInt2.end)
				return new PredicateInterval( new Predicate("fi", predInt1.getPredicate(), predInt2.getPredicate()), predInt2.start, predInt2.end);
			break;

			
		// Thresholded relations
		case m_window:		//meets within some window
			if( predInt2.start >= predInt1.end && predInt2.start - predInt1.end <= WINDOW_SIZE)
				return new PredicateInterval( new Predicate("m_window", predInt1.getPredicate(), predInt2.getPredicate()), predInt1.end, predInt2.start);
			break;
		}
		
		return null;
		
	}
}